﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

namespace Medianamik.Core.DAL
{
    internal class NodePropertyLoader
    {
        public IEnumerable<NodeProperty> LoadNodePropertyValues(Node pNode, 
            IList<NodePropertyValueLite> properties)
        {
            var factory = new NodePropertyFactory();

            var cultures = properties.Select(p => p.CultureName)
                .Union(pNode.Instances.Select(i => i.Culture.Name))
                .Union(new[] { CultureInfo.InvariantCulture.Name })
                .Distinct(StringComparer.OrdinalIgnoreCase);

            foreach (var culture in cultures)
            {
                foreach (var prop in GetLookupProperties(factory, pNode, properties, culture))
                {
                    yield return prop;
                }
                foreach (var prop in GetRegularProperties(factory, pNode, properties, culture))
                {
                    yield return prop;
                }
                foreach (var prop in GetManyToOneProperties(factory, pNode, properties, culture))
                {
                    yield return prop;
                }
            }

            foreach (var prop in GetManyToManyProperties(factory, pNode))
            {
                yield return prop;
            }
        }

        private IEnumerable<NodeProperty> GetLookupProperties(NodePropertyFactory pFactory, Node pNode,
            IList<NodePropertyValueLite> pDbProps, string pCultureName)
        {
            if (pNode.NodeType == null)
            {
                throw new CoreException("Type doesn't exist");
            }
            bool isLanguageNeutralFilter = pCultureName.Equals(CultureInfo.InvariantCulture.Name, StringComparison.InvariantCultureIgnoreCase);
            var filteredDbProps = pDbProps.Where(p => p.CultureName.EqualsIgnoreCase(pCultureName));

            var oneToManyProps = (from r in pNode.NodeType.GetOneToManyRelations()
                                  join dbProp in filteredDbProps on r.ForeignProperty.ID equals dbProp.PropertyId into g
                                  from dbProp in g.DefaultIfEmpty()
                                  let values = dbProp ?? new NodePropertyValueLite()
                                  where r.ForeignProperty.IsLanguageNeutral == isLanguageNeutralFilter
                                  select pFactory.CreateNodeProperty(values.NodePropertyValueId, values.Value, pNode, pCultureName, values.Revision, r));
            //Ne pas faire de ToArray ou ToList pour des questions de performance

            return oneToManyProps;

        }

        private IEnumerable<NodeProperty> GetRegularProperties(NodePropertyFactory pFactory, 
            Node pNode, IList<NodePropertyValueLite> pDbProps, string pCultureName)
        {
            if (pNode.NodeType == null)
            {
                throw new CoreException("Type doesn't exist");
            }

            var isLanguageNeutralFilter = pCultureName.Equals(CultureInfo.InvariantCulture.Name, StringComparison.InvariantCultureIgnoreCase);

            var filteredDbProps = pDbProps.Where(p => p.CultureName.EqualsIgnoreCase(pCultureName));

            var regularProps = (from p in pFactory.GetRegularNodeTypeProperties(pNode.NodeType)
                                join dbProp in filteredDbProps on p.ID equals dbProp.PropertyId into g
                                from dbProp in g.DefaultIfEmpty()
                                let values = dbProp ?? new NodePropertyValueLite()
                                where p.IsLanguageNeutral == isLanguageNeutralFilter
                                select pFactory.CreateNodeProperty(values.NodePropertyValueId, values.Value, pNode, p, pCultureName, values.Revision));
            //Ne pas faire de ToArray ou ToList pour des questions de performance

            return regularProps;
        }

        private IEnumerable<NodeProperty> GetManyToOneProperties(NodePropertyFactory pFactory,
            Node pNode, IEnumerable<NodePropertyValueLite> pDbProps, string pCultureName)
        {
            if (pNode.NodeType == null)
            {
                throw new CoreException("Type doesn't exist");
            }

            var isLanguageNeutralFilter = pCultureName.Equals(CultureInfo.InvariantCulture.Name, StringComparison.InvariantCultureIgnoreCase);

            var filteredDbProps = pDbProps.Where(p => p.CultureName.EqualsIgnoreCase(pCultureName));

            var manyToOneProperties = (from p in pFactory.GetManyToOneNodeTypeProperties(pNode.NodeType)
                                join dbProp in filteredDbProps on p.ID equals dbProp.PropertyId into g
                                from dbProp in g.DefaultIfEmpty()
                                let values = dbProp ?? new NodePropertyValueLite()
                                where p.IsLanguageNeutral == isLanguageNeutralFilter
                                select pFactory.CreateNodeProperty(values.NodePropertyValueId, values.Value, pNode, p, pCultureName, values.Revision));
            //Ne pas faire de ToArray ou ToList pour des questions de performance

            return manyToOneProperties;
        }

        private IEnumerable<NodeProperty> GetManyToManyProperties(NodePropertyFactory pFactory, 
            Node pNode)
        {
            return pNode.NodeType.GetManyToManyRelations()
                .Select(r => pFactory.CreateNodeProperty(pNode, r));
        }
    }
}
